import path from 'node:path'
import send from './serveSend'

// serve接受两个参数
// 第一个参数是路径地址
// 第二个是配置选项
export function serve(root, opts = {}) {
  const serveRoot = path.normalize(path.resolve(root))
  console.log('serveRoot = ' + serveRoot)
  // 这行代码如果效果就是
  // 如果没传opts，opts就是空对象{}
  // 同时将它的原型置为null
  opts = {
    ...opts,
    // 这里的root就是我们配置的静态文件目录，比如public
    root: serveRoot,
    // 就是http缓存控制Cache-Control的那个 maxAge
    maxAge: (opts.maxAge || 0) / 1000,
    // 也是Cache-Control缓存控制的
    immutable: opts.immutable || false,
    // format默认是true，用来支持/directory这种不带/的文件夹请求
    format: !!opts.format,
  }

  // 如果用户没有配置index，默认index就是index.html
  if(opts.index !== false) opts.index = opts.index || 'index.html'
  // Cache-Control
  const maxAge = (opts.maxAge || 0) / 1000
  const immutable = opts.immutable || false
  const directives = [`max-age=${maxAge}`]
  if(immutable) directives.push('immutable')
  opts.cacheControl = directives.join(',')

  // 如果defer为false，就用之前的逻辑，最后调用next
  if(!opts.defer) {
    /**
     * 返回值是一个符合koa中间件的范式： (ctx, next) => {}
     * @param {Context} ctx
     * @param {function} next
     *
     */
    return async function serve(ctx, next) {
      let done = false    // 这个变量标记文件是否成功返回

      // 只有HEAD和GET请求才响应
      if(ctx.method === 'HEAD' || ctx.method === 'GET') {
        try {
          done = await send(ctx, ctx.pathname, opts)
        } catch(err) {
          // 如果不是404，可能是一些400，500这种非预期的错误，将它抛出去
          if(err.status !== 404) {
            throw err
          }
        }
      }

      // 如果没成功，就让后续中间件继续处理他
      // 如果成功了，本次请求就到此为止了
      if(!done) {
        await next()
      }
    }
  }

  // 如果defer为true，先调用next，然后执行自己的逻辑
  return async function serve(ctx, next) {
    await next()

    if(ctx.method !== 'HEAD' && ctx.method !== 'GET') return

    // 如果ctx.body有值了，或者status不是404，说明请求已经被其他中间件处理过了，就直接返回了
    if(ctx.body != null || ctx.status !== 404) return // eslint-disable-line

    try {
      await send(ctx, ctx.pathname, opts)
    } catch(err) {
      if(err.status !== 404) {
        throw err
      }
    }
  }
}
